package channel

import (
	"gitee.com/lipore/go-nio/pkg/buffer"
	"gitee.com/lipore/go-nio/pkg/frame"
	"io"
)

type (
	OutboundContext interface {
		Context
		io.Writer
		OutboundBuffer() *buffer.Buffer
		bindRaw(f frame.Frame)
		frame() frame.Frame
		transportWrite(p []byte) (int, error)
	}
	OutboundHandler interface {
		HandleOutbound(ctx OutboundContext) error
	}
)

type outboundContext struct {
	commonContext
	buf buffer.Buffer
}

func (o *outboundContext) Write(p []byte) (n int, err error) {
	return o.buf.Write(p)
}

func (o *outboundContext) bindRaw(f frame.Frame) {
	_, _ = o.buf.Write(f)
}

func (o *outboundContext) frame() frame.Frame {
	if o.buf.Len() > 0 {
		f := make([]byte, o.buf.Len())
		_, err := o.buf.Read(f)
		if err != nil {
			return nil
		}
		return f
	}
	return nil
}

func (o *outboundContext) reset(ch Channel, handlers []Handler) {
	o.commonContext.reset(ch, handlers)
	o.index = int8(len(o.handlers))
}

func (o *outboundContext) OutboundBuffer() *buffer.Buffer {
	return &o.buf
}

func (o *outboundContext) Next() error {
	o.index--
	for o.index >= 0 {
		if h, ok := o.handlers[o.index].(OutboundHandler); ok {
			err := h.HandleOutbound(o)
			if err != nil {
				return err
			}
		}
		o.index--
	}
	return nil
}

func (o *outboundContext) transportWrite(p []byte) (int, error) {
	return o.channel.Transport().Write(p)
}
